# Author::    Lucas Carlson (lucas at rufy dot com)
# Author::    Thomas Nichols  (thomas at nexus10 dot com)
# Copyright:: Copyright (c) 2005-2006 Lucas Carlson, Thomas Nichols
# License::   Distributes under the same terms as Ruby

require 'cgi'
require 'csv'

require 'payment/base'

# NOTE: A patch is required to the Payment gem 1.0.1 as follows:

# --- base.rb.orig        2006-01-25 09:26:04.000000000 +0000
# +++ base.rb     2006-05-30 19:10:50.000000000 +0100
# @@ -49,7 +49,8 @@
#               http.use_ssl      = true
#               http.verify_mode = OpenSSL::SSL::VERIFY_NONE
#            end
# -          @response_plain = http.post(uri.path, @data).body
# +          headers = {'Content-Type' => 'application/x-www-form-urlencoded'}
# +          @response_plain = http.post(uri.path, @data, headers).body
#            @response       = @response_plain.include?('<?xml') ? REXML::Document.new(@response_plain) : @response_plain

#            @response.instance_variable_set "@response_plain", @response_plain


module Payment

  class VelocityPay < Base
    attr_reader :server_response, :avs_code, :order_number, :response_message
    attr_accessor :print_debug, :first_name, :last_name, :card_name, :expiry_month, :expiry_year, :card_cv2, :transaction_id,
      :auth_code, :invoice_num, :description, :house_number, :street
    
    # version of the VelocityPay API  
    API_VERSION = '4.0.0'
    
    # Set some sensible defaults for VelocityPay
    #    transaction = Payment::VelocityPay.new (
    #                    :login       => 'username',
    #                    :password    => 'password',
    #                    :amount      => '49.95',
    #                    :card_number => '4012888818888',
    #                    :expiration  => '03/10',
    #                    :first_name  => 'John',
    #                    :last_name   => 'Doe'
    #                   )
    #
    def initialize(options = {})
      # set some sensible defaults
      #@url = 'https://www.velocitypay.co.uk/merchantSecure/velocitypay/VPDirect.cfm'          # -- LIVE server
      #@url = 'https://hermes.velocitygroup.co.uk/merchantSecure/velocitypay/VPDirect.cfm'   # -- VP DEBUG server

      @print_debug = false
      @type = 'ESALE_KEYED'
      @callback_url = 'disable'
      @country = '826'
      @currency_code = '826'
      @duplicate_delay = '30'             # seconds to delay before allowing resubmission
      @echo_amount = 'YES'
      @echo_card_type = 'YES'
      @fraud_check='YES'
      @get_fraud_message = 'YES'
      @get_fraud_result = 'YES'
      @merchant_country = '826'
      @emv_terminal_type = '32'
      @transaction_id = 'TX-' + Time.now.xmlschema
      
      # include all provided data
      super options
    end
    
    # Submit the order to be processed. If it is not fulfilled for any reason
    # this method will raise an exception.
    def submit
      set_post_data
      print "\n@data is: " unless @print_debug == false
      print @data 
      get_response @url
      print "\nget_response ok\nresponse is: \n" unless @print_debug == false
      print @response unless @print_debug == false
      parse_response
    end
    
    private
    
    def set_post_data
      prepare_data
      
      post_array = Array.new
      FIELDS.each do |loc_var, gate_var|
        if @required.include?(loc_var) && eval("@#{loc_var}").nil?
          raise PaymentError, "The required variable '#{loc_var}' was left empty"
        else
          value = eval "CGI.escape(@#{loc_var}.to_s)"
          post_array << "#{gate_var}=#{value}" unless value.empty?
        end
      end

      @data = post_array.join('&')
    end

    def parse_response
      # Response string is a URL-encoded set of name/value pairs, we'll generate a hash of these pairs
      data = @response.plain.split('&').unshift nil    # TODO: check why a nil is prepended, test handling of embedded & in string
      result_hash = {}
      data.each do |nv_pair|
        name, value = nv_pair.split('=') unless nv_pair.nil?
        result_hash[name] = value
      end

      @result_code = result_hash['VPResponseCode'].to_i
      @response_message = result_hash['VPMessage']
      @avs_code = result_hash['VPAVSCV2ResponseCode']    # 6-digit value, see GatewayDirect400.pdf for explanation
      @transaction_id = result_hash['VPTransactionUnique']
      # The 'amount received' response field is currently (v4.0.0) misspelt..
      @amount_received = result_hash['VPAmountRecieved']
      # ... but they may correct it later:
      @amount_received = result_hash['VPAmountReceived'] unless @amount_received != nil

      if @result_code == 0 # Approved/Pending/Test
        return @transaction_id
      else
        @error_code = result_code
        @error_message = response_message
        raise PaymentError, @error_message
      end

    end   
    
    # make sensible changes to data
    def prepare_data

      @card_number = @card_number.to_s.gsub(/[^\d]/, "") unless @card_number.nil?
      @amount = (@amount * 100).to_i
      # expiry date should be as two digits
      short_year = @expiry_year.to_i
      short_year = short_year -2000 unless short_year < 2000
      @expiry_year = sprintf("%02d", short_year)
      
      
      @expiry_month = sprintf("%02d", @expiry_month.to_i)
      
      #@country = 
      
      # convert the action -- 'normal_authorization' becomes 'ESALE_KEYED'
      if TYPES.include?(@type)
        @type = TYPES[@type]
      elsif ! TYPES.has_value?(@type)
        raise PaymentError, "The type '#{@type}' is not valid"
      end

      auth_code = transaction_id if auth_code.nil?
        
      # add any required fields specific to all VelocityPay transactions - auth_code is VPOrderDesc
      @required += %w(password login amount country currency_code  type transaction_id auth_code callback_url)
      
      if @type == 'ESALE_KEYED'
        # mandatory fields for internet CC transaction
        @required += %w(card_name card_number expiry_year expiry_month)
        
      else
        raise PaymentError, "Can't handle transaction method: #{@type}"
      end
      @required.uniq!
    end
    

    # map the instance variable names to the gateway's requested variable names
    FIELDS = {
      # Custom VP-only fields.
      # site settings:
      'merchant_country' => 'VPCountryCode',              # 826 for UK transactions
      'callback_url' => 'VPCallBack',                     # must be 'disable'
      'fraud_check' => 'VPAVSCV2Check',                   # set to 'YES' or 'NO'
      'cross_reference' => 'VPCrossReference',            # not usually required - see VP docs
      'emv_terminal_type' => 'VPEMVTerminalType',         # set by VP - use 32 for internet transactions
      'duplicate_delay' => 'VPDuplicateDelay',            # seconds to delay before accepting a re-submit
      'get_fraud_result' => 'VPEchoAVSCV2ResponseCode',   # YES or NO - return result of fraud check?
      'get_fraud_message' => 'VPReturnAVSCV2Message',     # YES or NO - return messages for fraud-check failure?

      # per-transaction settings:
      'card_name' => 'VPCardName',
      'card_cv2' => 'VPCV2',                              # 3-digit card verification code
      'echo_amount' => 'VPEchoAmount',                    # YES or NO - return 'Amount' field to verify?
      'products' => 'VPProducts',                         # List of products bought - accessible in TMS
      'echo_card_type' => 'VPEchoCardType',               # YES or NO - return card type in VPCardType?
      'expiry_month' => 'VPExpiryDateMM',
      'expiry_year' => 'VPExpiryDateYY',
      'start_month' => 'VPStartDateMM',
      'start_year' => 'VPStartDateYY',
      'issue_number' => 'VPIssueNumber',
      'house_number' => 'VPBillingHouseNumber',
      'street' => 'VPBillingStreet',


      # Standard fields:
      'type' => 'VPMessageType',                          # typically 'ESALE_KEYED'
      'login' => 'VPMerchantID',
      'password' => 'VPMerchantPassword',
      'amount' => 'VPAmount',
      'currency_code' => 'VPCurrencyCode',            # 826 for sterling
      'transaction_id' => 'VPTransactionUnique',
      'auth_code' => 'VPOrderDesc',                   # Printed on CC statement
      'city' => 'VPBillingCity',
      'state' => 'VPBillingState',
      'zip' => 'VPBillingPostCode',
      'country' => 'VPBillingCountry',                       # Not mentioned in VP docs...
      'phone' => 'VPBillingPhoneNumber',
      'email' => 'VPBillingEmail',
      'card_number' => 'VPCardNumber'

    }





    # map the types to the merchant's action names
    TYPES = {     # Currently only an internet-sale transaction type is supported:
      'normal_authorization' => 'ESALE_KEYED'
    }

  end
end

